<?php

/**
 * Vault
 *
 * A hackish implementation of the singleton and registry patterns to allow for a common
 * store of instances to core warlock objects, also provides a loader function
 * for including libraries etc ..
 *
 * Warlock is free software. This version may have been modified pursuant
 * to the GNU General Public License, and as distributed it includes or
 * is derivative of works licensed under the GNU General Public License or
 * other free or open source software licenses.
 *
 * @package    warlock
 * @subpackage vault
 * @author     David Cole <neophyte@sourcetutor.com>
 * @license    GNU/GPL v2
 *
 */

using('Library.Patterns.Object');

class Vault
{
    /**
     * Initialise Class Singleton Behaviour
     *
     * This method is called by the main classes constructor, upon being called
     * a reference to the current instance of the class is added to the singleton
     * registry. If a record for the class already exists then an error is thrown.
     *
     * @return void
     * @access protected
     */
    function Add($name, &$instance)
    {
        // Check for existing class instance
        $registry = &Vault::_getRegistry();

        if (isset($registry[strtolower($name)]))
            trigger_error('Vault :: Class \''.$name.'\' has already been insantiated.', E_USER_ERROR);

        // New instance, add self to the registry
        $registry[strtolower($name)] = &$instance;

        if (isset($registry['debug_error']))
            $registry['debug_error']->Log('Vault :: Initialising \''.$name.'\'', W_LOG_DEBUG);
    }

    /**
     * Get Instance
     *
     * This method will retrieve an instance of the specified class, or create a new instance
     * if the class cant be located in the registry. Typically this method should only be called
     * when creating an initial instance via Singleton::instance('class_name', ...) getting an
     * instance to an already instanciated class should be done via the chained instance method.
     *
     * @return mixed reference to the object on success, else on failure false.
     * @access public
     */
    function &Get()
    {
        $registry = &Vault::_getRegistry();

        $args = func_get_args();

        // Ensure a valid class is specified
        if (count($args) == 0) {
            trigger_error('Missing class name in Vault::Get()', E_USER_ERROR);
            return '';
        }

        $class    = array_shift($args);

        if (isset($registry[strtolower($class)]))
            return $registry[strtolower($class)]; // Return reference to located class instance
        else
        {
        	// Create a list of args to be sent to the class constructor
            array_walk($args, create_function('&$v,$k', '$v = "\'".$v."\'";'));
            $args = implode(',', $args);

            // Ensure the class exists
            if (!class_exists($class))
                Vault::Load('Engine.'.$class);

            // Create Instance and return reference to class
            eval("new $class($args);");
	        return $registry[strtolower($class)];
        }
    }

    /**
     * Load
     *
     * Attempts to include the specified dot seperated file
     * relative to the warlock root.
     *
     * @return bool
     * @access public
     */
    function Load($path)
    {
        static $_loaded;

        if (!isset($_loaded))
            $_loaded = array();

        if (!isset($_loaded[$path]))
        {
			$_loaded[$path] = true;

			if (substr($path, -1) == '*')
			{
                if (is_dir($dir = str_replace('.', '/', substr($path, 0, -1))))
                {
                    $dh = opendir($dir);
        
                    while (($file = readdir($dh)) !== false)
                        if ($file != '.' && $file != '..' && is_file($dir.'/'.$file))
                            include $dir.'/'.$file;
        
                    closedir($dh);
                }			    
			}
			else 
			{
    			if (file_exists($file = W_ENV_PATH.'/'.str_replace('.', '/', $path).'.php'))
    			    include $file;
    			else
    			{
    			    $trace = debug_backtrace();
    
    			    if ((isset($trace[1]['function'])) && ($trace[1]['function'] == 'using'))
    			        trigger_error('Vault :: Error locating \''.str_replace(W_ENV_PATH, '', $file).'\' specified by \''.str_replace(W_ENV_PATH, '', $trace[1]['file']).':'.$trace[1]['line'].'\'', E_USER_ERROR);
    			    else
    			        trigger_error('Vault :: Error locating \''.str_replace(W_ENV_PATH, '', $file).'\' specified by \''.str_replace(W_ENV_PATH, '', $trace[0]['file']).':'.$trace[0]['line'].'\'', E_USER_ERROR);
    			}
			}
        }

        return true;
    }

    /**
     * Get Registry
     *
     * Returns a reference to the registry array holding all the currently instanced
     * objects.
     *
     * @return array
     * @access private
     */
    function &_getRegistry()
    {
        static $_registry;

        if (!isset($_registry))
            $_loaded = array();

        return $_registry;
    }
}

/**
 * Intelligent file importer
 *
 * @access public
 * @param string $path A dot syntax path
 */
function using($path)
{
	return Vault::Load($path);
}

?>
